package controller

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// HLSController HLS流媒体控制器
type HLSController struct{}

// HLSConfig HLS配置
type HLSConfig struct {
	SegmentDuration int    // 片段时长（秒）
	OutputDir       string // 输出目录
	BaseURL         string // 基础URL
}

var hlsConfig = HLSConfig{
	SegmentDuration: 10,
	OutputDir:       "./static/hls",
	BaseURL:         "/api/hls",
}

// ConvertToHLS 将视频转换为HLS格式
func (h *HLSController) ConvertToHLS(c *gin.Context) {
	// 获取上传的视频文件
	file, header, err := c.Request.FormFile("video")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 1,
			"msg":  "获取视频文件失败: " + err.Error(),
		})
		return
	}
	defer file.Close()

	// 生成唯一的视频ID
	videoID := fmt.Sprintf("video_%d", time.Now().Unix())

	// 创建输出目录
	outputDir := filepath.Join(hlsConfig.OutputDir, videoID)
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 1,
			"msg":  "创建输出目录失败: " + err.Error(),
		})
		return
	}

	// 保存上传的视频文件
	inputPath := filepath.Join(outputDir, "input"+filepath.Ext(header.Filename))
	outputFile, err := os.Create(inputPath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 1,
			"msg":  "创建输入文件失败: " + err.Error(),
		})
		return
	}
	defer outputFile.Close()

	if _, err := io.Copy(outputFile, file); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 1,
			"msg":  "保存视频文件失败: " + err.Error(),
		})
		return
	}

	// 使用FFmpeg转换为HLS
	m3u8Path := filepath.Join(outputDir, "playlist.m3u8")
	segmentPath := filepath.Join(outputDir, "segment_%03d.ts")

	cmd := exec.Command("ffmpeg",
		"-i", inputPath,
		"-c:v", "libx264", // 视频编码器
		"-c:a", "aac", // 音频编码器
		"-hls_time", strconv.Itoa(hlsConfig.SegmentDuration), // 片段时长
		"-hls_list_size", "0", // 保留所有片段
		"-hls_segment_filename", segmentPath, // 片段文件名模板
		"-f", "hls", // 输出格式
		m3u8Path, // 输出播放列表
	)

	// 执行转换
	if err := cmd.Run(); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 1,
			"msg":  "视频转换失败: " + err.Error(),
		})
		return
	}

	// 删除原始输入文件
	os.Remove(inputPath)

	// 返回播放列表URL
	playlistURL := fmt.Sprintf("%s/%s/playlist.m3u8", hlsConfig.BaseURL, videoID)

	c.JSON(http.StatusOK, gin.H{
		"code": 0,
		"msg":  "转换成功",
		"data": gin.H{
			"video_id":     videoID,
			"playlist_url": playlistURL,
			"segments_dir": fmt.Sprintf("%s/%s", hlsConfig.BaseURL, videoID),
		},
	})
}

// GetPlaylist 获取HLS播放列表
func (h *HLSController) GetPlaylist(c *gin.Context) {
	videoID := c.Param("video_id")
	if videoID == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 1,
			"msg":  "视频ID不能为空",
		})
		return
	}

	// 构建播放列表文件路径
	playlistPath := filepath.Join(hlsConfig.OutputDir, videoID, "playlist.m3u8")

	// 检查文件是否存在
	if _, err := os.Stat(playlistPath); os.IsNotExist(err) {
		c.JSON(http.StatusNotFound, gin.H{
			"code": 1,
			"msg":  "播放列表不存在",
		})
		return
	}

	// 读取播放列表内容
	content, err := os.ReadFile(playlistPath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 1,
			"msg":  "读取播放列表失败: " + err.Error(),
		})
		return
	}

	// 修改播放列表中的片段URL为完整URL
	playlistContent := string(content)
	lines := strings.Split(playlistContent, "\n")

	var modifiedLines []string
	for _, line := range lines {
		if strings.HasSuffix(line, ".ts") {
			// 将相对路径转换为完整URL
			segmentURL := fmt.Sprintf("%s/%s/%s", hlsConfig.BaseURL, videoID, line)
			modifiedLines = append(modifiedLines, segmentURL)
		} else {
			modifiedLines = append(modifiedLines, line)
		}
	}

	modifiedContent := strings.Join(modifiedLines, "\n")

	// 设置正确的Content-Type
	c.Header("Content-Type", "application/vnd.apple.mpegurl")
	c.Header("Access-Control-Allow-Origin", "*")
	c.String(http.StatusOK, modifiedContent)
}

// GetSegment 获取HLS视频片段
func (h *HLSController) GetSegment(c *gin.Context) {
	videoID := c.Param("video_id")
	segmentName := c.Param("segment")

	if videoID == "" || segmentName == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 1,
			"msg":  "参数不完整",
		})
		return
	}

	// 安全检查：确保segment名称只包含允许的字符
	if !strings.HasSuffix(segmentName, ".ts") {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 1,
			"msg":  "无效的片段文件名",
		})
		return
	}

	// 构建片段文件路径
	segmentPath := filepath.Join(hlsConfig.OutputDir, videoID, segmentName)

	// 检查文件是否存在
	if _, err := os.Stat(segmentPath); os.IsNotExist(err) {
		c.JSON(http.StatusNotFound, gin.H{
			"code": 1,
			"msg":  "视频片段不存在",
		})
		return
	}

	// 设置正确的Content-Type
	c.Header("Content-Type", "video/mp2t")
	c.Header("Access-Control-Allow-Origin", "*")
	c.Header("Cache-Control", "max-age=3600") // 缓存1小时

	// 直接返回文件内容
	c.File(segmentPath)
}

// ListVideos 获取所有可用的视频列表
func (h *HLSController) ListVideos(c *gin.Context) {
	// 读取HLS目录
	entries, err := os.ReadDir(hlsConfig.OutputDir)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 1,
			"msg":  "读取视频目录失败: " + err.Error(),
		})
		return
	}

	var videos []gin.H
	for _, entry := range entries {
		if entry.IsDir() {
			videoID := entry.Name()
			playlistPath := filepath.Join(hlsConfig.OutputDir, videoID, "playlist.m3u8")

			// 检查是否存在播放列表
			if _, err := os.Stat(playlistPath); err == nil {
				// 获取文件信息
				info, _ := entry.Info()

				videos = append(videos, gin.H{
					"video_id":     videoID,
					"playlist_url": fmt.Sprintf("%s/%s/playlist.m3u8", hlsConfig.BaseURL, videoID),
					"created_at":   info.ModTime().Format("2006-01-02 15:04:05"),
				})
			}
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"code": 0,
		"msg":  "获取成功",
		"data": videos,
	})
}

// DeleteVideo 删除指定的视频及其片段
func (h *HLSController) DeleteVideo(c *gin.Context) {
	videoID := c.Param("video_id")
	if videoID == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": 1,
			"msg":  "视频ID不能为空",
		})
		return
	}

	// 构建视频目录路径
	videoDir := filepath.Join(hlsConfig.OutputDir, videoID)

	// 检查目录是否存在
	if _, err := os.Stat(videoDir); os.IsNotExist(err) {
		c.JSON(http.StatusNotFound, gin.H{
			"code": 1,
			"msg":  "视频不存在",
		})
		return
	}

	// 删除整个视频目录
	if err := os.RemoveAll(videoDir); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"code": 1,
			"msg":  "删除视频失败: " + err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code": 0,
		"msg":  "删除成功",
	})
}
